package fnord.jovian.tango;

import java.awt.*;
import java.awt.geom.*;
import java.util.*;

import fnord.illuminati.card.*;
import fnord.jovian.TheBoard;
import fnord.jovian.tango.TheCompassLayout.TheCompassOrientation;

public class TheCard extends TheButton {

	protected TheTooltip _tooltip;
	protected Point _point, _clickPoint, _upPoint;
	protected boolean _showTooltip = false, _showShadow = false, _tempOrientL = false, _tempOrientO = false;
	public enum TheCardOrientation {NORTH,SOUTH,EAST,WEST};
	public enum TheCardInOut {IN,OUT,BLANK};
	protected TheCardOrientation _orientation = TheCardOrientation.SOUTH,  _oldOrientation;
	protected double _rotation = 0, _dragX = 0, _dragY = 0, _oldX, _oldY;
	protected int[] _sides;
	protected TheCard[] _children = new TheCard[4];
	protected TheCard _parent;
	protected TheCardInOut[] _inOut;
	protected Vector<TheObject> _triangles;
	protected Vector<TheOrientation> _orientations;
	protected TheLayout _layout = new TheCompassLayout(2);
	protected GroupCard _card;
	protected TheBoard _board;
	


	public TheCard(String title) {
		super(title,0,0);
	}

	public TheCard(String title, TheCardOrientation orientation, TheCardInOut[] inOut, TheBoard board) {
		super(title,TheTangoConstants.CARDWIDTH,TheTangoConstants.CARDHEIGHT);
		_title = new TheText(title,TheTangoConstants.CARDFONTSIZE,TheTangoConstants.CARDWIDTH,TheTangoConstants.CARDHEIGHT,true,true);
		_title.setPadding(10);
		setOrientation(orientation);
		_tooltip = new TheTooltip(board, null);
		_triangles = new Vector<TheObject>();
		_orientations = new Vector<TheOrientation>();
		inOut(inOut);
		_board = board;
	}

	public TheCard(GroupCard card, TheCardOrientation orientation, TheCardInOut[] inOut, TheBoard board) {
		super(card.getName(),TheTangoConstants.CARDWIDTH,TheTangoConstants.CARDHEIGHT);
		_title = new TheText(card.getName(),TheTangoConstants.CARDFONTSIZE,TheTangoConstants.CARDWIDTH,TheTangoConstants.CARDHEIGHT,true,true);
		_title.setPadding(10);
		_card = card;
		setOrientation(orientation);
		_tooltip = new TheTooltip(board, card);
		_triangles = new Vector<TheObject>();
		_orientations = new Vector<TheOrientation>();
		inOut(inOut);
		_board = board;
	}

	public GroupCard getCard() {
		return _card;
	}

	private void inOut(TheCardInOut[] inOut) {
		_inOut = inOut;
		TheCompassOrientation[] orient = new TheCompassOrientation[] {
				TheCompassOrientation.WEST,
				TheCompassOrientation.SOUTH,
				TheCompassOrientation.EAST,
				TheCompassOrientation.NORTH,
		};
		for(int i = 0; i < _inOut.length; i++) {
			switch(_inOut[i]) {
			case BLANK:
				_children[i] = new TheBlankCard();
				break;
			case IN:
				addTriangle(new TheTriangle(rotation(childOrientation(i))),orient[i]);
				_children[i] = new TheBlankCard();
				break;
			case OUT:
				addTriangle(new TheTriangle(Math.PI + rotation(childOrientation(i))),orient[i]);
				break;
			}
		}
	}

	public void setOrientation(TheCardOrientation orientation) {
		_rotation = rotation(orientation);
		_orientation = orientation;
	}

	public double rotation(TheCardOrientation orientation) {
		switch(orientation) {
		case SOUTH:
			return -Math.PI/2.0;
		case EAST:
			return Math.PI;
		case NORTH:
			return Math.PI/2.0;
		case WEST:
			return 0;
		}
		return 0;
	}

	public TheCardOrientation getOrientation() {
		return _orientation;
	}
	
	public TheMiniCard getMiniCard() {
		return new TheMiniCard(_title.getText(),_orientation,_inOut,_board,false);
	}

	public boolean contains(Point p) {
		_point = p;
		if (0 != _rotation) {
			double x = _shape.getCenterX();
			double y = _shape.getCenterY();
			java.awt.geom.AffineTransform trans = java.awt.geom.AffineTransform.getRotateInstance(_rotation, x, y);
			java.awt.Shape s = trans.createTransformedShape(_shape);
			return s.contains(p);
		}
		return super.contains(_point);
	}

	public void paint(Graphics2D g) {
		g.rotate(_rotation,_shape.getCenterX(), _shape.getCenterY());
		g.setFont(new Font("Arial", Font.PLAIN, (int)Math.round(5.0 * Toolkit.getDefaultToolkit().getScreenResolution() / 72.0)));
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		if (_showShadow) {
			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f));
		}
		g.setColor(_background);
		g.fill(_shape);
		g.setColor(new Color(20,20,20));
		g.setStroke(new java.awt.BasicStroke(2));
		g.draw(_shape);
		_title.paint(g);
		_layout.layout(_triangles, _orientations, _shape.getX(), _shape.getY(), _shape.getWidth(), _shape.getHeight());
		for (int i = 0; i < _triangles.size(); i++) {
			_triangles.get(i).paint(g);
		}
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
		g.rotate(-_rotation,_shape.getCenterX(), _shape.getCenterY());
	}

	public void addTriangle(TheTriangle o, TheOrientation or) {
		_triangles.add(o);
		_orientations.add(or);
	}

	public void paintTooltip(Graphics2D g) {
		if (_showTooltip)
			_tooltip.paint(g);
	}

	public void containAction() {
		_tooltip.setLocation(_point.getX() + 10, _point.getY() + 15);
		_showTooltip = true;
		_showShadow = false;
	}

	public void outAction() {
		for (int i = 0; i < _children.length; i++) {
			if (_children[i] != null) {
				_children[i].outAction();
			}
		}
		_showTooltip = false;
		_showShadow = false;
	}

	public TheCardOrientation inSide() {
		for (int i = 0; i < _inOut.length; i++) {
			if (_inOut[i] == TheCardInOut.IN)
				return childOrientation(i);
		}
		return null;
	}

	@Override
	public double getHeight() {
		return _shape.getHeight();
	}

	@Override
	public double getWidth() {
		return _shape.getWidth();
	}

	public Rectangle2D getRotatedBox() {
		switch(_orientation) {
		case SOUTH:
		case NORTH:
			return new Rectangle2D.Double(_shape.getCenterX() - _shape.getHeight()/2.0,
					_shape.getCenterY() - _shape.getWidth()/2.0,
					_shape.getHeight(),
					_shape.getWidth());
		case EAST:
		case WEST:
			return _shape.getBounds2D();
		}
		return null;
	}

	public Point2D.Double getInPoint() {
		Rectangle2D box = getRotatedBox();
		Point2D.Double[] points = new Point2D.Double[] {
				new Point2D.Double(_shape.getCenterX(), _shape.getCenterY() + box.getHeight()/2.0),
				new Point2D.Double(_shape.getCenterX() + box.getWidth()/2.0, _shape.getCenterY()),
				new Point2D.Double(_shape.getCenterX(), _shape.getCenterY() - box.getHeight()/2.0),
				new Point2D.Double(_shape.getCenterX() - box.getWidth()/2.0, _shape.getCenterY())
		};
		for (int i = 0; i < _inOut.length; i++) {
			if (_inOut[i] == TheCardInOut.IN)
				return points[(i + child(_orientation) + 4) % 4];
		}
		return null;
	}

	public Vector<ThePointWrapper> getOutPoints() {
		Rectangle2D box = getRotatedBox();
		Point2D.Double[] points = new Point2D.Double[] {
				new Point2D.Double(_shape.getCenterX(), _shape.getCenterY() + box.getHeight()/2.0),
				new Point2D.Double(_shape.getCenterX() + box.getWidth()/2.0, _shape.getCenterY()),
				new Point2D.Double(_shape.getCenterX(), _shape.getCenterY() - box.getHeight()/2.0),
				new Point2D.Double(_shape.getCenterX() - box.getWidth()/2.0, _shape.getCenterY())
		};
		Vector<ThePointWrapper> outPoints = new Vector<ThePointWrapper>();
		for (int i = 0; i < _inOut.length; i++) {
			if (_inOut[i] == TheCardInOut.OUT && _children[i] == null)
				outPoints.add(new ThePointWrapper(points[(i + child(_orientation) + 4) % 4],this,childOrientation(i)));
		}
		return outPoints;
	}

	public boolean addChild(TheCard card, TheCardOrientation side) {
		int s = child(side);
		if (s == -1)
			return false;
		if (_children[s] != null)
			return false;
		else
			_children[s] = card;
		return true;
	}

	public boolean removeChild(TheCardOrientation side) {
		int s = child(side);
		if (s == -1)
			return false;
		if (_children[s] == null)
			return false;
		else
			_children[s] = null;
		return true;
	}

	public void setParent(TheCard card) {
		_parent = card;
	}

	public String getTitle() {
		return _title.getText();
	}

	public TheCard getParent() {
		return _parent;
	}

	public TheCard getChild(TheCardOrientation side) {
		if (child(side) == -1) {
			return null;
		}
		return _children[child(side)];
	}

	public TheCard[] getChildren() {
		return _children;
	}

	public int child(TheCardOrientation side) {
		switch(side) {
		case SOUTH:
			return 0;
		case EAST:
			return 1;
		case NORTH:
			return 2;
		case WEST:
			return 3;
		default:
			return -1;
		}
	}

	public int side(TheCardOrientation side) {
		switch(side) {
		case SOUTH:
			return 0;
		case EAST:
			return 1;
		case NORTH:
			return 2;
		case WEST:
			return 3;
		default:
			return -1;
		}
	}

	public TheCardOrientation childOrientation(int side) {
		switch(side) {
		case 0:
			return TheCardOrientation.SOUTH;
		case 1:
			return TheCardOrientation.EAST;
		case 2:
			return TheCardOrientation.NORTH;
		case 3:
			return TheCardOrientation.WEST;
		default:
			return null;
		}
	}

	public int findChild(TheCard child) {
		for (int i = 0; i < _children.length; i++) {
			if (_children[i] == child)
				return i;
		}
		return -1;
	}

	public void clickAction() {
		_showShadow = true;
	}

	@Override
	public boolean containsClick(Point p) {
		for (int i = 0; i < _children.length; i++) {
			if (_children[i] != null) {
				_children[i].showShadow();
				_children[i].containsClick(p);
			}
		}
		_dragX = p.x - _shape.getX();
		_dragY = p.y - _shape.getY();
		_clickPoint = p;
		if (0 != _rotation) {

			double x = _shape.getCenterX();
			double y = _shape.getCenterY();
			java.awt.geom.AffineTransform trans = java.awt.geom.AffineTransform.getRotateInstance(_rotation, x, y);
			java.awt.Shape s = trans.createTransformedShape(_shape);
			return s.contains(p);
		}
		return super.containsClick(p);
	}

	@Override
	public void upAction() {
		for (int i = 0; i < _children.length; i++) {
			if (_children[i] != null) {
				_children[i].upAction();
			}
		}
		if (_clickPoint.equals(_upPoint) && _shape.contains(_clickPoint)) {
			if (!_card.getUsed()) {
				if (_board.transferable()) {
					_board.transferableAction(this);
				}
			}
			if (_board.attack()) {
				_board.attackAction(this);
			}
			_board.cardAction(this);
		}
		_showShadow = false;
		_showTooltip = false;
	}

	@Override
	public boolean containsUp(Point p) {
		_upPoint = p;
		if (0 != _rotation) {

			double x = _shape.getCenterX();
			double y = _shape.getCenterY();
			java.awt.geom.AffineTransform trans = java.awt.geom.AffineTransform.getRotateInstance(_rotation, x, y);
			java.awt.Shape s = trans.createTransformedShape(_shape);
			return s.contains(p);
		}
		return super.containsClick(p);
	}

	@Override
	public boolean containsDrag(Point p) {
		for (int i = 0; i < _children.length; i++) {
			if (_children[i] != null) {
				_children[i].containsDrag(p);
			}
		}
		_point = p;
		if (0 != _rotation) {
			double x = _shape.getCenterX();
			double y = _shape.getCenterY();
			java.awt.geom.AffineTransform trans = java.awt.geom.AffineTransform.getRotateInstance(_rotation, x, y);
			java.awt.Shape s = trans.createTransformedShape(_shape);
			return s.contains(p);
		}
		return super.contains(_point);
	}

	@Override
	public void dragAction() {
		if (_showShadow) {
			for (int i = 0; i < _children.length; i++) {
				if (_children[i] != null) {
					_children[i].showShadow();
					_children[i].dragAction();
				}
			}
			setLocation(_point.getX() - _dragX,_point.getY() - _dragY);
		}
		_showTooltip = false;
	}

	public void showShadow() {
		_showShadow = true;
	}

	public void reOrientLocation(double x, double y) {
		if (!_tempOrientL) {
			_oldX = _shape.getX();
			_oldY = _shape.getY();
			setLocation(x,y);
			_tempOrientL = true;
		}
	}

	public void reOrientOrientation(TheCardOrientation orientation) {
		if (!_tempOrientO) {
			_oldOrientation = _orientation;
			setOrientation(orientation);
			_tempOrientO = true;
		}
	}

	public void finalizeReOrient() {
		_tempOrientL = false;
		_tempOrientO = false;
	}

	public void revertReOrient() {
		if (_tempOrientL)
			setLocation(_oldX,_oldY);
		if (_tempOrientO)
			setOrientation(_oldOrientation);
		finalizeReOrient();
	}

	public boolean reOriented() {
		return _tempOrientL;
	}

	public boolean isDragged() {
		return _showShadow;
	}
	
	public void setUsed(boolean used) {
		_card.setUsed(used);
	}
}
